package Parse;

use strict;
use warnings;
use utf8;
use lib './src';

use Template;

use Vo::ModelVo;
use Vo::ModelPropertyVo;
use Vo::CustomEnumVo;
use Vo::EnumItemVo;

use SchemaVo::AggregateVo;
use SchemaVo::ModelVo;
use SchemaVo::ModelPropertyVo;

use Exporter qw(import);
our @EXPORT = qw(GetAggregatesWithoutEnum GetEnums GetAggregateVo);

sub GetAggregatesWithoutEnum {
    my @lines = @_;

    my $aggregateDefStart = 0;
    my $aggregateDefEnd = 0;
    my $aggregateCommentStart = 0;
    my $aggregateCommentEnd = 0;
    my $aggregatePackageFind = 0;
    my $aggregatePackagePrefixFind = 0;
    my $aggregatePackage = '';
    my $aggregatePackgePrefix = '';
    my $companyName = '';
    my $projectName = '';

    # 正则表达式匹配
    my $packagePrefixReg = qr/\!define\s+PPrefix\s+(((\w+)\.?)+)\s*/;
    my $packageReg = qr/package\s+(PPrefix\.)?(((\w+)\.?)+)\s*{/;
    my $classReg = qr/class\s+(\w+)\s*(<<Entity::(\w+)>>)?\s*(<<IAggregateRoot>>)?\s*{/;
    my $propertyReg = qr/^\s*[+\-#~]\s?(\w+)\s?:\s+(\S+)(\s?=\s?([a-zA-Z0-9.!]+))?\s+\/\/\s+(.*)/;

    my @aggregateList = ();
    my $aggregate;
    my $properties = [];

    my $configStart = 0;
    my $configEnd = 0;
    my $configStartReg = qr/^'ConfigStart\s*$/;
    my $configReg = qr/^'\$\s*([^\s=]+)(\s*=\s*([^\s]+))?\s*$/;
    my $configEndReg = qr/^'ConfigEnd\s*$/;
    my $config = {};

    foreach my $line (@lines) {
        if(!$configStart && $line =~ /$configStartReg/){
            $configStart = 1;
        }
        if($configStart && !$configEnd && $line =~ /$configEndReg/){
            $configEnd = 1;
        }
        if($configStart && !$configEnd && $line =~ /$configReg/){
            if($2){
                $config->{$1} = $3;
            } else {
                $config->{$1} = 1;
            }
        }
        if(!$aggregatePackagePrefixFind && $line =~ /$packagePrefixReg/){
            $aggregatePackagePrefixFind = 1;
            $aggregatePackgePrefix = $1;
            my @parts = split /\./, $aggregatePackgePrefix;
            $companyName = $parts[0];
            if($companyName eq 'Empty'){
                $companyName = undef;
            }
            $projectName = $parts[1];
            if($projectName eq 'Empty'){
                $projectName = undef;
            }
            $aggregatePackgePrefix = join('.', @parts[2..$#parts])
        }
        if (!$aggregatePackageFind && $line =~ /$packageReg/){
            $aggregatePackage = 1;
            $aggregatePackage = $2;
        }
        if (!$aggregateDefStart && $line =~ /$classReg/){
            $aggregateDefStart = 1;
            my $stronglyId = "$1Id";
            if(defined $2){
               $stronglyId = $3;
            }
            my $isAggregate = undef;
            if(defined $4){
                $isAggregate = 1;
            }
            $aggregate = Vo::ModelVo->new(
                Name => $1,
                StronglyId => $stronglyId,
                CompanyName => $companyName,
                ProjectName => $projectName,
                AggregatePackage => $aggregatePackgePrefix.'.'.$aggregatePackage,
                IsAggregate => $isAggregate,
                Config => $config,
            );
            next;
        }
        if($aggregateDefStart && !$aggregateDefEnd && $line =~ /^\s*}/){
            $aggregateDefEnd = 1;
            next;
        }
        if(!$aggregateCommentStart && $aggregateDefStart && $aggregateDefEnd )
        {
            $aggregate->setProperty($properties);
            $properties = [];
            if($line =~ /note right/){
                $aggregateCommentStart = 1;
            } else {
                $aggregateDefStart = 0;
                $aggregateDefEnd = 0;
                push @aggregateList, $aggregate;
                if($line =~ /$classReg/){
                    $aggregateDefStart = 1;
                    my $stronglyId = "$1Id";
                    if(defined $2){
                       $stronglyId = $3;
                    }
                    my $isAggregate = undef;
                    if(defined $4){
                        $isAggregate = 1;
                    }
                    $aggregate = Vo::ModelVo->new(
                        Name => $1,
                        StronglyId => $stronglyId,
                        AggregatePackage => $aggregatePackgePrefix.'.'.$aggregatePackage,
                        IsAggregate => $isAggregate,
                        Config => $config,
                    );
                }
            }
            next;
        }
        if(!$aggregateCommentEnd && $aggregateCommentStart && $line =~ /end note/){
            $aggregateCommentEnd = 1;
            next;
        }
        if($aggregateCommentEnd){
            $aggregateDefStart = 0;
            $aggregateDefEnd = 0;
            $aggregateCommentStart = 0;
            $aggregateCommentEnd = 0;
            push @aggregateList, $aggregate;
            if($line =~ /$classReg/){
                $aggregateDefStart = 1;
                my $stronglyId = "$1Id";
                if(defined $2){
                   $stronglyId = $3;
                }
                my $isAggregate = undef;
                if(defined $4){
                    $isAggregate = 1;
                }
                $aggregate = Vo::ModelVo->new(
                    Name => $1,
                    StronglyId => $stronglyId,
                    AggregatePackage => $aggregatePackgePrefix.'.'.$aggregatePackage,
                    IsAggregate => $isAggregate,
                    Config => $config,
                );
            }
        }

        if($aggregateDefStart && !$aggregateDefEnd && $line =~ /$propertyReg/){
            push @$properties, Vo::ModelPropertyVo->new(
                Type => $2, 
                Name => $1, 
                Comment => $5, 
                DefaultValue => $4,
                Config => $config,
            ); 
            next;
        }

        if($aggregateCommentStart && !$aggregateCommentEnd){
            $line =~ s/^\s*|\s*$//g;
            $aggregate->setComment($line);
            next;
        }

    }

    return @aggregateList;

}

sub GetEnums {
    my @lines = @_;
    my $enumStart = 0;
    my $enumEnd = 0;
    my $enumCommentStart = 0;
    my $enumCommentEnd = 0;

    my @enums = ();
    my $enum;

    my $enumItems = [];

    foreach my $line (@lines){
        if (!$enumStart && $line =~ /enum\s+(\w+)\s+<<New>>\s?{/){
            $enumStart = 1;
            $enum = Vo::CustomEnumVo->new(
                Name => $1
            );
            next;
        }
        if ($enumStart && !$enumEnd && $line =~ /^\s*}/){
            $enumEnd = 1;
            next;
        }
        if (!$enumCommentStart && $enumStart && $enumEnd ){
            $enum->{Items} = $enumItems;
            $enumItems = [];
            if( $line=~ /note right/){
                $enumCommentStart = 1;
            } else {
                $enumStart = 0;
                $enumEnd = 0;
                push @enums, $enum;
                if($line =~ /enum\s+(\w+)\s+<<New>>\s?{/){
                    $enumStart = 1;
                    $enum = Vo::CustomEnumVo->new(
                        Name => $1
                    );
                }
            }
            next;
        }
        if (!$enumCommentEnd && $enumCommentStart && $line =~ /end note/){
            $enumCommentEnd = 1;
            next;
        }
        if ($enumCommentEnd){
            $enumStart = 0;
            $enumEnd = 0;
            $enumCommentStart = 0;
            $enumCommentEnd = 0;
            push @enums, $enum;
            if($line =~ /enum\s+(\w+)\s+<<New>>\s?{/){
                $enumStart = 1;
                $enum = Vo::CustomEnumVo->new(
                    Name => $1
                );
            }
            next;
        }
        if ($enumStart && !$enumEnd && $line =~ /^\s*(\w+)\s?=\s?(\d+)\s+\/\/\s?(.*)/){
            push @$enumItems, Vo::EnumItemVo->new(Key => $1, Value => $2, Comment => $3);
            next;
        }
        if ($enumCommentStart && !$enumCommentEnd){
            $line =~ s/^\s*|\s*$//g;
            $enum->setComment($line);
            next;
        }

    }

    return @enums;
}

# 获取聚合值对象
sub GetAggregateVo {
    my ($companyName, $projectName, $lines) = @_;

    my $modelDefStart = 0;
    my $modelDefEnd = 0;
    my $modelCommentStart = 0;
    my $modelCommentEnd = 0;

    my $aggregateNameFound = 0;

    # 聚合名称
    my $aggregateName = '';

    # 聚合名称正则表达式
    my $aggregateNameReg = qr/package\s+(PPrefix\.)?(((\w+)\.?)+)\s*{/;
    # 模型定义正则表达式
    my $modelReg = qr/class\s+(\w+)\s*(<<Entity::(\w+)>>)?\s*(<<IAggregateRoot>>)?\s*{/;
    # 属性定义正则表达式
    my $propertyReg = qr/^\s*[+\-#~]\s?(\w+)\s?:\s+(\S+)(\s?=\s?([a-zA-Z0-9.!]+))?\s+\/\/\s+(.*)/;

    # 聚合值对象
    my $aggregateVo;

    # 模型列表
    my @modelVoList = ();
    # 模型值对象
    my $modelVo;
    # 属性列表
    my $propertyVos = [];

    foreach my $line (@$lines) {

        # 匹配聚合名称
        if (!$aggregateNameFound && $line =~ /$aggregateNameReg/){
            $aggregateNameFound = 1;
            $aggregateName = $2;
        }

        # 创建聚合值对象
        if(!defined $aggregateVo && $aggregateNameFound){
            $aggregateVo = SchemaVo::AggregateVo->New(
                CompanyName => $companyName,
                ProjectName => $projectName,
                Name => $aggregateName,
            );
        }

        if (!$modelDefStart && $line =~ /$modelReg/){
            $modelDefStart = 1;
            my $identityName = "$1Id";
            if(defined $2){
               $identityName = $3;
            }

            my $isAggregate = 0;
            if(defined $4){
                $isAggregate = 1;
            }

            $modelVo = SchemaVo::ModelVo->New(
                IdentityName => $identityName,
                Name => $1,
                IsAggregate => $isAggregate,
            );
            next;
        }

        if($modelDefStart && !$modelDefEnd && $line =~ /^\s*}/){
            $modelDefEnd = 1;
            next;
        }

        if(!$modelCommentStart && $modelDefStart && $modelDefEnd )
        {
            $modelVo->{PropertyVos} = $propertyVos;
            $propertyVos = [];
            if($line =~ /note right/){
                $modelCommentStart = 1;
            } else {
                $modelDefStart = 0;
                $modelDefEnd = 0;
                push @modelVoList, $modelVo;
                if($line =~ /$modelReg/){
                    $modelDefStart = 1;
                    my $identityName = "$1Id";
                    if(defined $2){
                       $identityName = $3;
                    }
                    my $isAggregate = 0;
                    if(defined $4){
                        $isAggregate = 1;
                    }
                    $modelVo = SchemaVo::ModelVo->New(
                        IdentityName => $identityName,
                        Name => $1,
                        IsAggregate => $isAggregate,
                    );
                }
            }
            next;
        }

        if(!$modelCommentEnd && $modelCommentStart && $line =~ /end note/){
            $modelCommentEnd = 1;
            next;
        }

        if($modelCommentEnd){
            $modelDefStart = 0;
            $modelDefEnd = 0;
            $modelCommentStart = 0;
            $modelCommentEnd = 0;
            push @modelVoList, $modelVo;
            if($line =~ /$modelReg/){
                $modelDefStart = 1;
                my $identityName = "$1Id";
                if(defined $2){
                   $identityName = $3;
                }
                my $isAggregate = 0;
                if(defined $4){
                    $isAggregate = 1;
                }
                $modelVo = SchemaVo::ModelVo->New(
                    IdentityName => $identityName,
                    Name => $1,
                    IsAggregate => $isAggregate,
                );
            }
        }

        if($modelDefStart && !$modelDefEnd && $line =~ /$propertyReg/){
            push @$propertyVos, SchemaVo::ModelPropertyVo->New(
                ModelPropertyId => undef,
                Name => $1,
                DataType => $2,
                AccessType => '-',
                Comment => $5,
                DefaultValue => $4,
            );
            next;
        }

        if($modelCommentStart && !$modelCommentEnd){
            $line =~ s/^\s*|\s*$//g;
            $modelVo->{Comment} .= $line;
            next;
        }

    }

    $aggregateVo->{ModelVos} = \@modelVoList;

    return $aggregateVo;

}

1;
